/*
 *  shnodeui_qt.cpp
 *  shaderz_v3
 *
 *  Created by Anjali on 4/15/11.
 *  Copyright 2011 __MyCompanyName__. All rights reserved.
 *
 */

#include <QApplication>
#include <QtGui/QVBoxLayout>
#include <QtGui/QHBoxLayout>
#include <QtGui/QGroupBox>
#include <QtGui/QPainter>
#include <QtGui/QLabel>

#include "shnodeui_qt.h"

using namespace shaderz;
using namespace shaderzui::qt;

const int shNodeUI::WIDGETSIZE = 100;
const int linkStubUI::LINKSTUBSIZE = 10;

#include "shnode_color.h"
#include "shnodeuicolor_qt.h"
shNodeUI::shNodeUI( QWidget* InParent, shNode* InNode )
	: ParentClass(InParent,	Qt::Dialog |  Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint)
	, mPreviewWidget(0)
	, mNode(InNode)
{
	// mark this widget to be deleted when closed
	setAttribute( Qt::WA_DeleteOnClose );
	
	QVBoxLayout* pMainLayout = new QVBoxLayout;
	pMainLayout->setContentsMargins( 0, 0, 0, 0 );
		
	// preview widget
	mPreviewWidget = new nodePreviewWidget(this);
	mPreviewWidget->setMinimumSize( QSize(WIDGETSIZE, WIDGETSIZE) );
	pMainLayout->addWidget( mPreviewWidget );
	connect( mPreviewWidget, SIGNAL(clicked()), this, SLOT(onPreviewWidgetClicked()) );
	
	// stub section
	QGroupBox* pStubSection = new QGroupBox( tr("") );
	QHBoxLayout* pStubSectionLayout = new QHBoxLayout;
	pStubSectionLayout->setSpacing(0);
	pStubSectionLayout->setContentsMargins( 0, 0, 0, 0 );
	pStubSection->setLayout( pStubSectionLayout );
	pStubSection->setFlat(true);
	
	// input stubs
	std::vector<shNode::linkStub*>& inputStubs = InNode->inputStubs();
	if( inputStubs.size() > 0 )
	{
		QGroupBox* pInputStubGroup = new QGroupBox( tr(""), this );
		{
			// set layout
			QVBoxLayout* pInputStubLayout = new QVBoxLayout;
			pInputStubLayout->setContentsMargins( 0, 0, 0, 0 );
			pInputStubGroup->setLayout(pInputStubLayout);

			// add widgets
			for( int ipIndex=0; ipIndex < inputStubs.size(); ++ipIndex )
			{
				linkStubUI* pNewStub = new linkStubUI( this, inputStubs[ipIndex], linkStubUI::STUBALIGN_LEFT );
				pInputStubLayout->addWidget( pNewStub );
				connect( pNewStub, SIGNAL(stubInteraction(shStubInteraction, linkStubUI*)), this, SLOT(onStubInteraction(shStubInteraction, linkStubUI*)) );
				
				mAllStubs.push_back( pNewStub );
			}
		}
		pStubSectionLayout->addWidget( pInputStubGroup );
		pStubSectionLayout->setAlignment( pInputStubGroup, Qt::AlignLeft );
	}
	
	// output stubs
	std::vector<shNode::linkStub*>& outputStubs = InNode->outputStubs();
	if( outputStubs.size() > 0 )
	{
		QGroupBox* pOutputStubGroup = new QGroupBox( tr(""), this );
		{
			// set layout
			QVBoxLayout* pOutputStubLayout = new QVBoxLayout;
			pOutputStubLayout->setContentsMargins( 0, 0, 0, 0 );
			pOutputStubGroup->setLayout(pOutputStubLayout);
			
			// add widgets
			for( int opIndex=0; opIndex < outputStubs.size(); ++opIndex )
			{
				linkStubUI* pNewStub = new linkStubUI( this, outputStubs[opIndex], linkStubUI::STUBALIGN_RIGHT );
				pOutputStubLayout->addWidget( pNewStub );
				connect( pNewStub, SIGNAL(stubInteraction(shStubInteraction, linkStubUI*)), this, SLOT(onStubInteraction(shStubInteraction, linkStubUI*)) );
				
				mAllStubs.push_back( pNewStub );
			}
		}
		pStubSectionLayout->addWidget( pOutputStubGroup );
		pStubSectionLayout->setAlignment( pOutputStubGroup, Qt::AlignRight );
	}
	
	pMainLayout->addWidget( pStubSection );

	// set the main layout
	if( layout() )
	{	
		delete layout();
	}
	setLayout( pMainLayout );
}

shNodeUI::~shNodeUI()
{
	// emit the signal indicating that the node is being destroyed
	emit nodeUIDeleted((unsigned int)mNode);
	
	// the UI has taken ownership of the node; hence we need to delete it
	if( mNode )
	{
		delete mNode;
	}
}

void shNodeUI::onStubInteraction( shStubInteraction InInteraction, linkStubUI* InSelectedStubUI)
{
	// new connection will be handled at this level
	if( InInteraction == SHSI_TryNewConnection )
	{
		shNode* pDragOriginNode = node();
		const shNode::linkStub* pDragOriginStub = InSelectedStubUI->linkStub();
		
// Parenting may be a problem here... the hierarchy may include layouts also
#define FIND_PARENT( parentClass, startWidget, result ) \
			{	\
				result = 0;	\
				QObject* myParent = startWidget; \
				while( myParent && !result )	\
				{	\
					result = dynamic_cast<parentClass*>(myParent);	\
					myParent = myParent->parent();	\
				}	\
			}
		simpleStubWidget* pStubWidget = dynamic_cast<simpleStubWidget*>(QApplication::widgetAt( QCursor::pos() ));
		linkStubUI* pStubUI = 0;
		shNodeUI* pNodeUI = 0;
		FIND_PARENT( linkStubUI, pStubWidget, pStubUI );
		FIND_PARENT( shNodeUI, pStubUI, pNodeUI );
		shNode* pDragEndNode = pNodeUI ? pNodeUI->node() : 0;
		const shNode::linkStub* pDragEndStub = pStubUI ? pStubUI->linkStub() : 0;
		
		// cannot connect stubs of the same io type
		if( !pDragEndNode || !pDragEndStub || pDragOriginStub->ioType == pDragEndStub->ioType )
		{
			return;
		}
		
		const shNode::linkStub* pInputStub = pDragOriginStub->ioType == shNode::SHIO_INPUT ? pDragOriginStub : pDragEndStub;
		const shNode::linkStub* pOutputStub = pDragOriginStub->ioType == shNode::SHIO_OUTPUT ? pDragOriginStub : pDragEndStub;
		
		if( pDragOriginNode->areStubsCompatible(pOutputStub->type, pInputStub->type) )
		{
			pDragOriginNode->connect( pDragOriginStub->name, pDragEndNode, pDragEndStub->name );
		}
	}
	else
	{
		emit stubInteraction( InInteraction, InSelectedStubUI, this );
	}
}

void shNodeUI::onPreviewWidgetClicked()
{
	// nothing to do here
}

/**
 *	Finds the UI for the given link stub.
 */
linkStubUI* shNodeUI::findUIForStub( const shaderz::shNode::linkStub* InLinkStub ) const
{
	linkStubUI* pRequiredUIStub = 0;
	
	for( int stubIndex=0; stubIndex < mAllStubs.size(); ++stubIndex )
	{
		if( mAllStubs[stubIndex]->linkStub() == InLinkStub )
		{
			pRequiredUIStub = mAllStubs[stubIndex];
			break;
		}
	}
	
	return pRequiredUIStub;
}

void shNodeUI::moveEvent( QMoveEvent* InEventInfo )
{
	ParentClass::moveEvent( InEventInfo );
	
	// emit the signal corrosponding to moved ui
	emit nodeUIMoved();
}

/////////////////////////////////////////////////
// linkStubUI
linkStubUI::linkStubUI( QWidget* InParent, shNode::linkStub* InLinkStub, linkStubUI::alignMode InAlignMode )
	: ParentClass(InParent)
	, mLinkStub(InLinkStub)
	, mAlignMode( InAlignMode )
{
	QHBoxLayout* pMainLayout = new QHBoxLayout;
	pMainLayout->setContentsMargins( 0, 0, 0, 0 );

	mInternalStubWidget = new simpleStubWidget(this, QColor(0,0,255), QColor(0,255,0));
	mInternalStubWidget->setMinimumSize(LINKSTUBSIZE, LINKSTUBSIZE);
	mInternalStubWidget->setMaximumSize(LINKSTUBSIZE, LINKSTUBSIZE);
	
	if( InAlignMode == STUBALIGN_LEFT )
	{
		pMainLayout->addWidget( mInternalStubWidget );
		pMainLayout->addWidget( new QLabel( InLinkStub->name.c_str() ) );
	}
	else
	{
		pMainLayout->addWidget( new QLabel( InLinkStub->name.c_str() ) );
		pMainLayout->addWidget( mInternalStubWidget );
	}
	
	if( layout() )
	{
		delete layout();
	}
	setLayout( pMainLayout );
	
	// connect the signals so that the selection is propagated
	connect( mInternalStubWidget, SIGNAL(stubInteraction(shStubInteraction)), this, SLOT(onStubInteraction(shStubInteraction)) );
}

QPoint linkStubUI::getStubPos() const
{
	QPoint widgetPos = QPoint();
	if( mInternalStubWidget )
	{
		widgetPos = mInternalStubWidget->pos();
		widgetPos += QPoint( mInternalStubWidget->width()/2, mInternalStubWidget->height()/2 );
	}
	
	return widgetPos;
}

void linkStubUI::onStubInteraction(shStubInteraction InInteraction)
{
	emit stubInteraction( InInteraction, this);
}

/////////////////////////////////////////////////////
// simpleStub Widget
simpleStubWidget::simpleStubWidget(QWidget* InParent, const QColor& InNormalColor, const QColor& InHoverColor )
	: ParentClass(InParent)
	, mNormalColor(InNormalColor)
	, mHoverColor(InHoverColor)
	, mIsMouseOver(false)
{
	// track the mouse independent of the buttons being pressed
	setMouseTracking(true);
}

void simpleStubWidget::mousePressEvent( QMouseEvent* InEventInfo )
{
	ParentClass::mousePressEvent(InEventInfo);
	
	// emit the mouse down signal
	emit stubInteraction(SHSI_MousePressed);
}

void simpleStubWidget::mouseMoveEvent( QMouseEvent* InEventInfo )
{
	ParentClass::mouseMoveEvent(InEventInfo);
	
	// inform the parent(s) of the mouse move
	emit stubInteraction( SHSI_MouseMove );
}

void simpleStubWidget::mouseReleaseEvent( QMouseEvent* InEventInfo )
{
	emit stubInteraction( SHSI_MouseReleased );

	simpleStubWidget* pWidgetUnderMouse = dynamic_cast<simpleStubWidget*>( QApplication::widgetAt(QCursor::pos()) );
	if( pWidgetUnderMouse && pWidgetUnderMouse != this )
	{
		emit stubInteraction( SHSI_TryNewConnection );
	}
}

void simpleStubWidget::enterEvent( QEvent* InEventInfo )
{
	ParentClass::enterEvent( InEventInfo );
	
	emit stubInteraction( SHSI_MouseEnter );

	mIsMouseOver = true;
	update();
}

void simpleStubWidget::leaveEvent( QEvent* InEventInfo )
{
	ParentClass::leaveEvent(InEventInfo);
	
	// inform the parent of the leaving of mouse from this stub
	emit stubInteraction( SHSI_MouseLeave );
	
	mIsMouseOver = false;
	update();
}

void simpleStubWidget::paintEvent( QPaintEvent* InEventInfo )
{
	ParentClass::paintEvent(InEventInfo);
	
	// draw a solid color
	QPainter painter(this);
	QBrush solidBrush( mIsMouseOver ? mHoverColor : mNormalColor );
	painter.fillRect( rect(), solidBrush );
}


///////////////////////////////////////
// nodePreviewWidget
nodePreviewWidget::nodePreviewWidget( QWidget* InParent )
	: ParentClass(InParent)
	, mPreviewType( SHPT_Const )
{
}

void nodePreviewWidget::setPreviewType( PreviewType InPreviewType )
{
	mPreviewType = InPreviewType;
	update();
}

void nodePreviewWidget::setSolidColor( QColor InNewColor, bool InShouldUpdate )
{
	mSolidColor = InNewColor;
	
	if( InShouldUpdate )
	{
		update();
	}
}

void nodePreviewWidget::mouseReleaseEvent(QMouseEvent* InEventInfo)
{
	ParentClass::mouseReleaseEvent(InEventInfo);
	
	// emit the clicked signal
	emit clicked();
}

void nodePreviewWidget::paintEvent( QPaintEvent* InEventInfo )
{
	ParentClass::paintEvent( InEventInfo );
	
	switch( mPreviewType )
	{
	case SHPT_Color:
		{
			QPixmap pixelMap(128,128);
			pixelMap.fill( mSolidColor );

			QPainter painter(this);
			painter.drawPixmap( 0, 0, pixelMap );
		}
		break;
		
	default:
		{
			QPixmap pixelMap(128,128);
			pixelMap.fill( Qt::darkGreen );

			QPainter painter(this);
			painter.drawPixmap( 0, 0, pixelMap );
		}
		break;
	}
}